
#include <asset/assetsystem.h>
#include <asset/reader/assetreadereffect.h>
#include <fs/fileinfo.h>
#include <gfx/ieffect.h>
#include <gfx/ieffecttechnique.h>
#include <gfx/iprogram.h>
#include <gfx/levelofdetail.h>
#include <vfs.h>
#include <cffformat.h>

ceAssetReaderEffect::ceAssetReaderEffect()
{
}



bool ceAssetReaderEffect::CanRead(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	if (type != iEffect::AssetTypeID)
		{
			return false;
		}

	ceFileInfo info (locator.GetLocation());
	std::string ext = info.GetExtension();
	return ext == "fx";
}


cffNode* ceAssetReaderEffect::FindNode(cffNode *parent, const char *name)
{
	for (unsigned i=0, j=parent->GetNumberOfNodes(); i<j; ++i)
		{
			cffNode* effectNode = parent->GetNode(i);
			if (effectNode
					&& effectNode->GetName() == std::string("effect")
					&& effectNode->HasAttribute(0)
					&& std::string (name) == (const char*)effectNode->GetAttribute(0))
				{
					return effectNode;
				}

		}

	return 0;
}



ceRef<ceAsset> ceAssetReaderEffect::Read(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	ceReleasePtr<iFile> file (ceVFS::Get()->Open(locator.GetLocation()));
	if (!file)
		{
			return 0;
		}

	long size = file->GetLength();

	char* buffer = new char[size+1];
	file->Read(buffer, size);
	file->Close ();

	buffer[size] = '\0';

	cffParser parser;
	ceReleasePtr<cffNode> parent (parser.ParseFromBuffer(buffer));
  delete [] buffer;
	if (!parent)
		{
			return 0;
		}

	cffNode* effectNode = FindNode(parent, locator.GetName());
	if (!effectNode)
		{
			return 0;
		}

	iEffect* effect = engine->GetDevice()->CreateEffect();
  effect->SetLocator(locator);

	unsigned validTechniques = 0;
	ceAssetSystem* assetManager = engine->GetAssetSystem();


  cffNode* solverNode = effectNode->GetNode("solver");
  if (solverNode && solverNode->HasAttribute(0))
    {
      iLevelOfDetailSolver* solver = assetManager->GetAsset<iLevelOfDetailSolver>(ceAssetLocator(solverNode->GetAttribute(0)));
      effect->SetSolver(solver);

    }
	for (unsigned i=0, j=effectNode->GetNumberOfNodes("technique"); i<j; ++i)
		{
			cffNode* techniqueNode = effectNode->GetNode("technique", i);

			cffNode* requirementsNode = techniqueNode->GetNode("requirement");
			if (requirementsNode && !TestRequirements(requirementsNode))
				{
					continue;
				}

			iEffectTechnique* fxProg = engine->GetDevice()->CreateEffectProgram();

			cffNode* diffuseNode = techniqueNode->GetNode("diffuse");
			if (diffuseNode && diffuseNode->HasAttribute(0))
				{
					iProgram* diffuseProg = assetManager->GetAsset<iProgram>(ceAssetLocator(diffuseNode->GetAttribute(0)));
					if (!diffuseProg)
						{
							fxProg->Release();
							effect->Release();
							return 0;
						}
					fxProg->SetProgram(RP_Diffuse, diffuseProg);
				}

			cffNode* depthNode = techniqueNode->GetNode("depth");
			if (depthNode && depthNode->HasAttribute(0))
				{
					iProgram* depthProg = assetManager->GetAsset<iProgram>(ceAssetLocator(depthNode->GetAttribute(0)));
					if (!depthProg)
						{
							fxProg->Release();
							effect->Release();
							return 0;
						}
					fxProg->SetProgram(RP_Depth, depthProg);
				}
			cffNode* shadowNode = techniqueNode->GetNode("shadow");
			if (shadowNode && shadowNode->HasAttribute(0))
				{
					iProgram* shadowProg = assetManager->GetAsset<iProgram>(ceAssetLocator(shadowNode->GetAttribute(0)));
					if (!shadowProg)
						{
							fxProg->Release();
							effect->Release();
							return 0;
						}
					fxProg->SetProgram(RP_Shadow, shadowProg);
				}
			cffNode* stencilNode = techniqueNode->GetNode("stencil");
			if (stencilNode && stencilNode->HasAttribute(0))
				{
					iProgram* stencilProg = assetManager->GetAsset<iProgram>(ceAssetLocator(stencilNode->GetAttribute(0)));
					if (!stencilProg)
						{
							fxProg->Release();
							effect->Release();
							return 0;
						}
					fxProg->SetProgram(RP_StencilVolume, stencilProg);
				}

			effect->AddEffectTechnique(fxProg);
			fxProg->Release();
			++validTechniques;
		}

	if (validTechniques == 0)
		{
			effect->Release();
			effect = 0;
		}

	return effect;
}


bool ceAssetReaderEffect::TestRequirements(cffNode *requirementsNode)
{
	return true;
}
